using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using Northwoods.Go;
using Workflows.Components;

namespace Workflows.Diagrams
{
    /// <summary></summary>
    [Serializable]
    public class ActivityNode : GoIconicNode
    {
        #region members
        // private static System.Random myRandom;
        public const int ChangedActivityType = LastChangedHint + 12344;
        private WorkflowActivityType myActivityType = WorkflowActivityType.SimpleActivity;
        #endregion

        #region props
        private ActivityPort _InboundPort;
        /// <summary></summary>
        public ActivityPort InboundPort
        {
            get
            {
                return this._InboundPort;
            }
            set
            {
                this._InboundPort = value;
            }
        }

        private ActivityPort _ErrorPort;
        /// <summary></summary>
        public ActivityPort ErrorPort
        {
            get
            {
                return this._ErrorPort;
            }
            set
            {
                this._ErrorPort = value;
            }
        }

        private ActivityPort _OutboundPort;
        /// <summary></summary>
        public ActivityPort OutboundPort
        {
            get
            {
                return this._OutboundPort;
            }
            set
            {
                this._OutboundPort = value;
            }
        }
        #endregion

        #region ctor & init
        /// <summary></summary>
        public ActivityNode()
        {
            this.Editable = true;
        }

        /// <summary></summary>
        public void InitializeActivity(WorkflowComponentBase wfComponent, string name, string toolTip)
        {
            this.Text = name;
            this.UserObject = wfComponent;
            GoImage goImg = new GoImage();
            goImg.Image = wfComponent.ComponentImage;
            goImg.Width = 48;
            goImg.Height = 48;
            goImg.Selectable = false;
            GoText goLabel = new GoText();
            goLabel.Text = name;
            goLabel.Selectable = false;
            goLabel.Editable = true;
            this.Label = goLabel;
            this.Icon = goImg;

            if (wfComponent.Instruction != null)
            {
                List<string> inboundDataNames = new List<string>();
                //ProcessInputType inputType = wfComponent.InputType;
                inboundDataNames.Add(wfComponent.Instruction.GetType().Name);
                this.AddInboundPorts(inboundDataNames);
            }
            List<string> outboundDataNames = new List<string>();
            outboundDataNames.Add(wfComponent.OutputName);
            this.AddOutboundPorts(outboundDataNames);
            this.AddErrorPort();
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        public override string Text
        {
            get
            {
                if (this.UserObject != null && this.UserObject is WorkflowComponentBase)
                {
                    return ((WorkflowComponentBase) this.UserObject).ComponentName;
                }
                else
                    return base.Text;
            }
            set
            {
                if (this.UserObject != null && this.UserObject is WorkflowComponentBase)
                {
                    ((WorkflowComponentBase)this.UserObject).ComponentName = value;
                }
                else
                    base.Text = value;
            }
        }

        /// <summary></summary>
        protected override GoPort CreatePort()
        {
            if (this.Initializing)
                return null;

            ActivityPort p = new ActivityPort();
            return p;
        }

        
        /// <summary></summary>
        public GoPort AddPort(int id, SizeF iconOffset, int linkSpot)
        {
            GoPort p = this.CreatePort();
            if (p != null)
            {
                p.UserFlags = id;
                PointF pt = this.Icon.Position;
                p.Position = new PointF(pt.X + iconOffset.Width, pt.Y + iconOffset.Height);
                p.FromSpot = linkSpot;
                p.ToSpot = linkSpot;
                this.Add(p);
            }
            return p;
        }

        /// <summary></summary>
        public ActivityPort GetPort(int id)
        {
            foreach (GoObject obj in this)
            {
                ActivityPort p = null;
                if (obj.GetType() == typeof(ActivityPort))
                    p = (ActivityPort)obj;
                if (p != null && p.UserFlags == id)
                    return p;
            }
            return null;
        }

        protected override GoText CreateLabel(string name)
        {
            GoText l = base.CreateLabel(name);
            if (l != null)
            {
                l.Bold = true;
                l.TextColor = Color.DarkGreen;
                l.Editable = true;
            }
            return l;
        }

        protected override void OnLayerChanged(GoLayer oldlayer, GoLayer newlayer, GoObject mainObj)
        {
            base.OnLayerChanged(oldlayer, newlayer, mainObj);
            if (newlayer != null && (newlayer.Document.GetType() == typeof(ProcessDocument)))
            {
                this.DraggableLabel = true;
            }
        }

        protected override void OnChildBoundsChanged(GoObject child, RectangleF old)
        {
            if (child != null && child.Equals(this.Icon))
            {
                PointF newctr = child.Center;
                PointF oldctr = this.GetRectangleSpotLocation(old, Middle);
                Single offsetx = newctr.X - oldctr.X;
                Single offsety = newctr.Y - oldctr.Y;
                foreach (GoObject obj in this)
                {
                    if (!(obj.Equals(child) || obj.Equals(this.Label) || obj.Equals(this.Port)))
                    {
                        obj.Bounds = new RectangleF(obj.Left + offsetx, obj.Top + offsety, obj.Width, obj.Height);
                    }
                }
            }
            
            base.OnChildBoundsChanged(child, old);
        }

        private void AddInboundPorts(List<string> dataNames)
        {
            WorkflowComponentBase wfComponent = this.UserObject as WorkflowComponentBase;
            if(wfComponent != null && dataNames !=null && dataNames.Count>0)
            {
                this._InboundPort = this.CreatePort() as ActivityPort;
                if (this._InboundPort != null)
                {
                    Port inPort = new Port();
                    inPort.DataName = dataNames[0];
                    inPort.Direction = PortDirection.Inbound;
                    inPort.OwnerComponentID = wfComponent.ComponentID;
                    this._InboundPort.UserObject = inPort;
                    this._InboundPort.UserFlags = (int)PortDirection.Inbound;
                    this._InboundPort.IsValidFrom = false;
                    this._InboundPort.IsValidTo = true;
                    this._InboundPort.FromSpot = MiddleTop;
                    this._InboundPort.ToSpot = MiddleTop;
                    this._InboundPort.Pen = Pens.Black;
                    this._InboundPort.Brush = Brushes.Green;
                    this._InboundPort.SetSpotLocation(MiddleTop, this.Icon, MiddleTop);

                    this.Add(this._InboundPort);
                }
                if(dataNames.Count>1)
                {
                    for(int i=1;i<dataNames.Count;i++)
                    {
                        Single offsetx = (Single) 8 * i / 2;
                        if (i % 2 == 0)
                            offsetx = 0 - offsetx;
                        SizeF offset = new SizeF(offsetx, this.Icon.Top);
                        int spot = this.ComputePortLinkSpot(offset);
                        ActivityPort goPort2 = this.AddPort(i, offset, spot) as ActivityPort;
                        if (goPort2 != null)
                        {
                            Port inPort2 = new Port();
                            inPort2.DataName = dataNames[i];
                            inPort2.Direction = PortDirection.Inbound;
                            inPort2.OwnerComponentID = wfComponent.ComponentID;
                            goPort2.UserObject = inPort2;
                            goPort2.UserFlags = (int)PortDirection.Inbound;
                            goPort2.IsValidFrom = false;
                            goPort2.IsValidTo = true;
                            goPort2.MaxLinks = 1;
                            goPort2.Pen = Pens.Black;
                            goPort2.Brush = Brushes.Green;
                            goPort2.FromSpot = spot;
                            goPort2.ToSpot = spot;
                            goPort2.SetSpotLocation(spot, this.Icon, spot);

                            this.Add(this._InboundPort);
                        }
                    }
                }
            }
        }

        private void AddOutboundPorts(List<string> dataNames)
        {
            WorkflowComponentBase wfComponent = this.UserObject as WorkflowComponentBase;
            if (wfComponent != null && dataNames != null && dataNames.Count > 0)
            {
                this._OutboundPort = this.CreatePort() as ActivityPort;
                if (this._OutboundPort != null)
                {
                    Port outPort = new Port();
                    outPort.DataName = dataNames[0];
                    outPort.Direction = PortDirection.Outbound;
                    outPort.OwnerComponentID = wfComponent.ComponentID;
                    this._OutboundPort.UserObject = outPort;
                    this._OutboundPort.UserFlags = (int)PortDirection.Outbound;
                    this._OutboundPort.IsValidFrom = true;
                    this._OutboundPort.IsValidTo = false;
                    this._OutboundPort.FromSpot = MiddleBottom;
                    this._OutboundPort.ToSpot = MiddleBottom;
                    this._OutboundPort.Pen = Pens.Black;
                    this._OutboundPort.Brush = Brushes.Green;
                    this._OutboundPort.SetSpotLocation(MiddleBottom, this.Icon, MiddleBottom);

                    this.Add(this._OutboundPort);
                }
                if (dataNames.Count > 1)
                {
                    for (int i = 1; i < dataNames.Count; i++)
                    {
                        Single offsetx =(Single) 8 * i / 2;
                        if (i % 2 == 0)
                            offsetx = 0 - offsetx;
                        SizeF offset = new SizeF(offsetx, this.Icon.Bottom);
                        int spot = this.ComputePortLinkSpot(offset);
                        ActivityPort goPort2 = this.AddPort(i, offset, spot) as ActivityPort;
                        if (goPort2 != null)
                        {
                            Port outputPort2 = new Port();
                            outputPort2.DataName = dataNames[i];
                            outputPort2.Direction = PortDirection.Outbound;
                            outputPort2.OwnerComponentID = wfComponent.ComponentID;
                            goPort2.UserObject = outputPort2;
                            goPort2.IsValidFrom = true;
                            goPort2.IsValidTo = false;
                            goPort2.MaxLinks = 1;
                            goPort2.Pen = Pens.Black;
                            goPort2.Brush = Brushes.Green;
                            goPort2.FromSpot = spot;
                            goPort2.ToSpot = spot;
                            goPort2.SetSpotLocation(spot, this.Icon, spot);

                            this.Add(this._OutboundPort);
                        }
                    }
                }
            }
        }

        private void AddErrorPort()
        {
            WorkflowComponentBase wfComponent = this.UserObject as WorkflowComponentBase;
            this._ErrorPort = this.CreatePort() as ActivityPort;
            //PointF pos;
            //if (this.Icon != null)
            //    pos = this.Icon.Position;
            //else
            //    pos = this.Position;
            if (this._ErrorPort != null && wfComponent != null)
            {
                Port errorPort=new Port();
                errorPort.DataName = wfComponent.ComponentName + " Error";
                errorPort.Direction = PortDirection.Error;
                errorPort.OwnerComponentID = wfComponent.ComponentID;
                this._ErrorPort.UserObject = errorPort;
                this._ErrorPort.UserFlags = (int)PortDirection.Error;
                this._ErrorPort.IsValidFrom = true;
                this._ErrorPort.IsValidTo = false;
                this._ErrorPort.FromSpot = MiddleRight;
                this._ErrorPort.ToSpot = MiddleRight;
                this._ErrorPort.Pen = Pens.Black;
                this._ErrorPort.Brush = Brushes.Red;
                this._ErrorPort.SetSpotLocation(MiddleRight, this.Icon, MiddleRight);
                this.Add(this._ErrorPort);
            }
        }

        //public void AddInitialPorts(int portCount)
        //{
        //    object obj;
        //    this.Icon.Size = new SizeF(48, 48);
        //    PointF pos;
        //    if (this.Icon != null)
        //        pos = this.Icon.Position;
        //    else
        //        pos = this.Position;

        //    if (this.ActivityType == WorkflowActivityType.Trigger)
        //    {
        //        ActivityPort p = null;
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        if (p != null)
        //        {
        //            p.UserFlags =(int) ConnectorType.Trigger;
        //            p.IsValidFrom = true;
        //            p.IsValidTo = false;
        //            p.FromSpot = MiddleBottom;
        //            p.ToSpot = MiddleBottom;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Blue;
        //            p.SetSpotLocation(MiddleBottom, this.Icon, MiddleBottom);
        //            this.Add(p);
        //        }

        //        p = null;
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        if (p != null)
        //        {
        //            p.UserFlags = (int)ConnectorType.Failure;
        //            p.IsValidFrom = true;
        //            p.IsValidTo = false;
        //            p.FromSpot = MiddleRight;
        //            p.ToSpot = MiddleRight;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Red;
        //            p.SetSpotLocation(MiddleRight, this.Icon, MiddleRight);
        //            this.Add(p);
        //        }
        //    }
        //    else if (this.ActivityType == WorkflowActivityType.Finish)
        //    {
        //        ActivityPort p = null;
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        if (p != null)
        //        {
        //            p.UserFlags = (int)ConnectorType.Successful;
        //            p.IsValidFrom = false;
        //            p.IsValidTo = true;
        //            p.FromSpot = MiddleTop;
        //            p.ToSpot = MiddleTop;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Blue;
        //            p.SetSpotLocation(MiddleTop, this.Icon, MiddleTop);
        //            this.Add(p);
        //        }
        //    }
        //    else if (this.ActivityType == WorkflowActivityType.Exception)
        //    {
        //        ActivityPort p = null;
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        if (p != null)
        //        {
        //            p.UserFlags = (int)ConnectorType.Failure;
        //            p.IsValidFrom = false;
        //            p.IsValidTo = true;
        //            p.FromSpot = MiddleTop;
        //            p.ToSpot = MiddleTop;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Red;
        //            p.SetSpotLocation(MiddleTop, this.Icon, MiddleTop);
        //            this.Add(p);
        //        }
        //    }
        //    else if (this.ActivityType == WorkflowActivityType.Finish)
        //    {
        //        ActivityPort p = null;
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        if (p != null)
        //        {
        //            p.UserFlags = (int)ConnectorType.Successful;
        //            p.IsValidFrom = false;
        //            p.IsValidTo = true;
        //            p.FromSpot = MiddleTop;
        //            p.ToSpot = MiddleTop;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Green;
        //            p.SetSpotLocation(MiddleTop, this.Icon, MiddleTop);
        //            this.Add(p);
        //        }
        //    }
        //    else
        //    {
        //        // inbounds
        //        ActivityPort p = null;
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        if (p != null)
        //        {
        //            p.UserFlags = (int)ConnectorType.Successful;
        //            p.IsValidFrom = false;
        //            p.IsValidTo = true;
        //            p.FromSpot = MiddleTop;
        //            p.ToSpot = MiddleTop;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Green;
        //            p.SetSpotLocation(MiddleTop, this.Icon, MiddleTop);
        //            this.Add(p);
        //        }
        //        if (portCount > 1)
        //        {
        //            for (int i = 2; i <= portCount; i++)
        //            {
        //                Single offsetx = 8 * i / 2;
        //                if (i % 2 == 0)
        //                    offsetx = 0 - offsetx;
        //                SizeF offset = new SizeF(offsetx, this.Icon.Top);
        //                int spot = this.ComputePortLinkSpot(offset);
        //                obj = this.AddPort(i, offset, spot);
        //                if (obj.GetType() == typeof(ActivityPort))
        //                    p = (ActivityPort)obj;
        //                else
        //                    p = null;
        //                if (p != null)
        //                {
        //                    p.UserFlags = (int)ConnectorType.Successful;
        //                    p.IsValidFrom = false;
        //                    p.IsValidTo = true;
        //                    p.MaxLinks = 1;
        //                    p.Pen = Pens.Black;
        //                    p.Brush = Brushes.Green;
        //                    p.FromSpot = spot;
        //                    p.ToSpot = spot;
        //                    p.SetSpotLocation(spot, this.Icon, spot);
        //                    this.Add(p);
        //                }
        //            }
        //        }

        //        // outbounds
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        else
        //            p = null;
        //        if (p != null)
        //        {
        //            p.UserFlags = (int)ConnectorType.Successful;
        //            p.IsValidFrom = true;
        //            p.IsValidTo = false;
        //            p.FromSpot = MiddleBottom;
        //            p.ToSpot = MiddleBottom;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Green;
        //            p.SetSpotLocation(MiddleBottom, this.Icon, MiddleBottom);
        //            this.Add(p);
        //        }
        //        obj = this.CreatePort();
        //        if (obj.GetType() == typeof(ActivityPort))
        //            p = (ActivityPort)obj;
        //        else
        //            p = null;
        //        if (p != null)
        //        {
        //            p.UserFlags = (int)ConnectorType.Failure;
        //            p.IsValidFrom = true;
        //            p.IsValidTo = false;
        //            p.FromSpot = MiddleRight;
        //            p.ToSpot = MiddleRight;
        //            p.Pen = Pens.Black;
        //            p.Brush = Brushes.Red;
        //            p.SetSpotLocation(MiddleRight, this.Icon, MiddleRight);
        //            this.Add(p);
        //        }
        //    }
        //}
        /// <summary></summary>
        public int ComputePortLinkSpot(SizeF offset)
        {
            int spot = Middle;
            if (offset.Width > offset.Height)
            {
                if (offset.Height < this.Icon.Height - offset.Height)
                    spot = MiddleTop;
                else
                    spot = MiddleRight;
            }
            else
            {
                if (offset.Width > this.Icon.Height - offset.Height)
                    spot = MiddleBottom;
                else
                    spot = MiddleLeft;
            }
            return spot;
        }


        #region properties
        /// <summary></summary>
        public WorkflowActivityType ActivityType
        {
            get
            {
                return this.myActivityType;
            }
            set
            {
                WorkflowActivityType old = this.myActivityType;
                if (old != value)
                {
                    this.myActivityType = value;
                    this.Changed(ChangedActivityType, (int)old, null, NullRect, (int)value, null, NullRect);
                }
            }
        }
        #endregion

        #region overrides
        /// <summary></summary>
        public override bool OnContextClick(GoInputEventArgs evt, GoView view)
        {
            GoContextMenu cm = new GoContextMenu(view);
            cm.MenuItems.Add(new MenuItem("Rename", new EventHandler(this.Rename_Command)));
            cm.MenuItems.Add(new MenuItem("Adjust port position", new EventHandler(this.MovePorts_Command)));
            cm.Show(view, evt.ViewPoint);
            return base.OnContextClick(evt, view);
        }

        /// <summary></summary>
        public override void ChangeValue(GoChangedEventArgs e, bool undo)
        {
            switch (e.SubHint)
            {
                case ChangedActivityType:
                    this.ActivityType = (WorkflowActivityType)e.GetInt(undo);
                    break;
                default:
                    base.ChangeValue(e, undo);
                    break;
            }
        }
        #endregion

        #region context menu event handler
        private void Rename_Command(object sender, EventArgs e)
        {
            GoView v = GoContextMenu.FindView((MenuItem)sender);
            if (v != null)
            {
                this.Label.DoBeginEdit(v);
            }
        }

        private void MovePorts_Command(object sender, EventArgs e)
        {
            GoView v = GoContextMenu.FindView((MenuItem)sender);
            if (v != null)
            {
                v.Tool = new PortMovingTool(v);
            }
        }
        #endregion
    }

    
}
